1 /*
2  * Hunt - a framework for web and console application based on Collie using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module hunt.text.conf;
13 
14 import std.array;
15 import std.stdio;
16 import std.string;
17 import std.exception;
18 
19 class INIFormatExceptionException : Exception
20 {
21 	mixin basicExceptionCtors;
22 }
23 
24 class Ini
25 {
26     this()
27     {}
28     
29     this(string file)
30     {
31         setConfigFile(file);
32     }
33     
34     //pragma(inline, true)
35     void setConfigFile(string file)
36     {
37         _fileName = file;
38         readConf();
39     }
40     
41     auto get(T = string)(string path, T def = T.init)
42     {
43         import std.conv;
44         string str = _element.getValue(path);
45         if(str.length == 0)
46             return def;
47         else
48             return to!T(str);
49     }
50     
51     bool set(T)(string path, T value)
52     {
53         import std.conv;
54         if(path.length == 0) return false;
55         string[] list = split(path,'.');
56         size_t len = list.length - 1;
57         auto ele  = getElement(list[0..len]);
58         string key = list[len];
59         if(key.length == 0) return false;
60         ele.values[key] = to!string(value);
61         return true;
62     }
63     
64     pragma(inline, true)
65     bool save()
66     {
67         return saveToFile(_fileName);
68     }
69     
70     pragma(inline, true)
71     bool saveAs(string fileName)
72     {
73         return saveToFile(fileName);
74     }
75     
76 protected:
77     void readConf()
78     {
79         auto f = File(_fileName,"r");
80         if(_element)
81         {
82             _element.destroy;
83         }
84         _element = new Element();
85         if(!f.isOpen()) return;
86 		scope(exit) f.close();
87         Element ele = _element;
88         int line = 1;
89         while(!f.eof())
90         {
91             scope(exit) line += 1;
92             string str = f.readln();
93             str = strip(str);
94             if(str.length == 0) continue;
95             if(str[0] == '#' || str[0] == ';') continue;
96             auto len = str.length -1;
97             if(str[0] == '[' && str[len] == ']')
98             {
99                 string section = str[1..len].strip;
100                 string[] list = split(section,'.');
101                 ele = getElement(list);
102                 continue;
103             }
104             auto site = str.indexOf("=");
105 			enforce!INIFormatExceptionException((site > 0),format("the format is erro in file %s, in line %d",_fileName,line));
106             string key = str[0..site].strip;
107             string value  = str[site + 1..$].strip;
108             ele.values[key] = value;
109         }
110     }
111     
112     
113     Element getElement(in string[] list)
114     {
115         Element ele = _element;
116         size_t num = 1;
117         foreach (ref str ; list)
118         {
119             scope(exit) num += 1;
120             if(str.length == 0) continue;
121             auto tele = ele.elements.get(str, null);
122             if(tele is null)
123             {
124                 tele = new Element;
125                 tele.path = list[0..num].join('.');
126                 ele.elements[str] = tele;
127             }
128             ele = tele;
129         }
130         return ele;
131     }
132     
133     bool saveToFile(string file)
134     {
135         if(file.length == 0) return false;
136         auto f = File(file,"w");
137         if(!f.isOpen()) return false;
138         writeElement(&f,_element);
139         return true;
140     }
141     
142     void writeElement(File * file, Element ele)
143     {
144         if(ele.path.length > 0)
145         {
146             file.writeln(format(";The section = %s",ele.path));
147             file.writefln("[%s]",ele.path);
148         }
149         
150         foreach(key,value; ele.values)
151         {
152             file.writefln("%s = %s",key,value);
153         }
154         file.writeln("");
155         foreach(_,value; ele.elements)
156         {
157             writeElement(file,value);
158         }
159     }
160     
161 private:
162     string _fileName;
163     Element _element; 
164 }
165 
166 alias Conf = Ini;
167 
168 private:
169 final class Element
170 {
171     string[string] values;
172     
173     Element[string] elements;
174     
175     string path;
176     
177     string getValue(string key)
178     {
179         if(key.length == 0) return string.init;
180         string[] list = split(key,'.');
181         size_t len = list.length - 1;
182         Element ele = this;
183         for (size_t i = 0; i < len; ++i)
184         {
185             auto element = ele.elements.get(list[i], null);
186             if (element is null )  return string.init;
187             ele = element;
188         }
189         return ele.values.get(list[len], string.init);
190     }
191 } 
192 
193 unittest
194 {
195     string tbody = "[server] \nhost = 0.0.0.0 \nport = 8081 \n\n[upload_tmp_path]\ntempath = ./storage/tmp\n\n";
196     tbody ~= "[route] \npath = ./config/router.conf \n\n[log]  \nname = error,fatal,info\n\n[file_path]\nfilesDir = ./uploads\n;huioiujopp\n#guijj\n\n";
197     tbody ~= "[route.tmp]\npath = hujjokp\n\n";
198     auto f = File("tmp.conf","w");
199     f.write(tbody);
200     f.close();
201     Conf conf = new Conf("tmp.conf");
202     string host = conf.get("server.host");
203     assert(host == "0.0.0.0");
204     ushort us = conf.get!ushort("server.port");
205     assert(us == cast(ushort)8081);
206     host = conf.get("route.tmp.path");
207     assert(host == "hujjokp");
208     host = conf.get("upload_tmp_path.tempath");
209     assert(host == "./storage/tmp");
210     conf.set("aa.bb.cc.dd", 500);
211     assert(conf.saveAs("tmp.saveas.conf"));
212 }